/*
 * lrusimul.c
 * 
 * Copyright 2012 Francisco Krahe               <fkrahe@gmail.com>
 * 		  Pedro Beschorner Marin        <pedrobmarin@gmail.com>
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 * 
 * 
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "memory.h"

#define MAXLINE 80
#define FILE_DIR "./perf/"
#define FILE_LOG "log.txt"
#define FILE_EXT ".txt"

#define MEMSIZE "MEMSIZE"
#define PROCSIZE "PROCSIZE"
#define READ "READ"
#define WRITE "WRITE"
#define ENDPROC "ENDPROC"

/**
 * Join 2 strings
 * @param path with trailing /
 * @param fileName without leading /
 * @return the strings, joined
 */
char* assembleFullPath(char* path, char* fileName) {
    char *fullPath;
    // assemble file path
    fullPath = malloc(strlen(path) + strlen(fileName) + 1);
    strcpy(fullPath, FILE_DIR);
    strcat(fullPath, fileName);
    return fullPath;
}

/**
 * Takes linked list of PROCESSes and perform a
 * formated write to a given file
 * @param fifo
 * @param fileName
 */
void writeLog(PROCESS *fifo, char* fileName) {
    PROCESS* tmp;
    FILE* fd;

    tmp = fifo;
    fd = fopen(fileName, "w");
    while (tmp != NULL) {
        fprintf(fd, "PROCESSO %2d\n", tmp->processId);
        fprintf(fd, "Página Acessos(R/W) NroPageFault NroSubst\n");

        int i;
        for (i = 0; i < tmp->processSize; i++) {
            if (tmp->pageTable[i].accesses > 0) {
                fprintf(fd, "%3d     %3d            %2d        %2d  \n",
                        tmp->pageTable[i].number,
                        tmp->pageTable[i].accesses,
                        tmp->pageTable[i].faults,
                        tmp->pageTable[i].replaces);
            }
        }
        fprintf(fd, "\n");

        tmp = tmp->next;
    }
    fclose(fd);
}

/**
 * 
 * @param argc
 * @param argv
 * @return 
 */
int main(int argc, char** argv) {

    PROCESS *processList; // Valid process list
    PROCESS *endedProcessList; // Finished process list
    MEMORY *memory; // RAM
    int memorySize; // RAM size

    FILE *fd; /* configuration file descriptor */
    char *fileName;

    printf("argc is %d\n", argc);

    if (argc < 2) {
        printf("Usage: $lrusimul path/to/configFile.txt\n");
        return EXIT_FAILURE;
    }

    // first argv is program name
    // second argv is first parameter [filename]
    fileName = *++argv;

    // verify .txt extension
    char *ext = strstr(fileName, FILE_EXT);
    if (ext == NULL || strlen(ext) != 4) {
        printf("File extension is not .txt\n");
        return EXIT_FAILURE;
    }

    printf("Opening the file %s\n", fileName);

    // open file
    fd = fopen(fileName, "rt");

    // read file
    char line[MAXLINE];

    int arg1;
    int arg2;
    char operation [MAXLINE];

    // line 1
    if (fgets(line, MAXLINE, fd) != NULL) {
        sscanf(line, "%s %d", operation, &arg1);

        if (strcmp(operation, MEMSIZE) == 0) {
            memorySize = arg1;
            printf("Memory size is set to %d pages.\n", memorySize);
        } else {
            printf("Impossible to determine memory size, check your configuration file.\n");
            return EXIT_FAILURE;
        }
    }

    // We initialize RAM and 2 process lists
    memory = memoryCreate();
    processList = procListCreate();
    endedProcessList = procListCreate();

    // other lines 
    while (fgets(line, MAXLINE, fd) != NULL) {
        sscanf(line, "%s %d %d", operation, &arg1, &arg2);
        if (strcmp(operation, PROCSIZE) == 0) {
            procListInsert(&processList, arg1, arg2);
        } else if (strcmp(operation, READ) == 0) {
            memoryRead(processList, &memory, memorySize, arg2, arg1);
        } else if (strcmp(operation, WRITE) == 0) {
            memoryWrite(processList, &memory, memorySize, arg2, arg1);
        } else if (strcmp(operation, ENDPROC) == 0) {
            endProcListInsert(&endedProcessList, procRemove(&processList, &memory, arg1));
        } else if (strcmp(operation, "end") == 0) {
            printf("Empty line, gonna finish.\n");
            break;
        } else {
            printf("Unknown operation, check your configuration file.\n");
            return EXIT_FAILURE;
        }
        strcpy(operation, "end");
    }
    //memoryPrint(memory);

    // close file
    fclose(fd);

    // No fim, precisamos terminar todos os processos para que possamos
    // gravar os dados dos processos em um arquivo. Pois terminando todos
    // os processos é garantido que estarão todos em endedProcessList em
    // ordem crescente de processId
    while (!procListEmpty(processList)) {
        endProcListInsert(&endedProcessList, procRemove(&processList, &memory, processList->processId));
    }
    //procListPrint(endedProcessList);

    // file opening
    writeLog(endedProcessList, assembleFullPath(FILE_DIR, FILE_LOG));

    return EXIT_SUCCESS;
}

